package luxing.common.dao.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.CriteriaImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import luxing.common.dao.CommonDao;
import luxing.exception.BusinessException;
import luxing.hibernate.CriteriaQuery;
import luxing.util.PageUtil;
import luxing.web.model.SortDirection;

/**
 * 公共扩展方法
 *
 * @author zzc
 *
 */
@Repository
public class CommonDaoImpl<T, PK extends Serializable> implements CommonDao {

    @Autowired
    @Qualifier("sessionFactory")
    private SessionFactory sessionFactory;

    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    public Session getSession() {
        // 事务必须是开启的(Required)，否则获取不到
        return sessionFactory.getCurrentSession();
    }

    @Override
    public <T> Serializable save(T entity) {
        return getSession().save(entity);
    }

    @Override
    public <T> void saveOrUpdate(T entity) {
        getSession().saveOrUpdate(entity);
    }

    @Override
    public <T> void batchSave(List<T> entitys) {
        for (int i = 0; i < entitys.size(); i++) {
            getSession().save(entitys.get(i));
            if (i % 1000 == 0) {
                // 1000个对象批量写入数据库，后才清理缓存
                getSession().flush();
                getSession().clear();
            }
        }
        // 最后页面的数据，进行提交手工清理
        getSession().flush();
        getSession().clear();
    }

    @Override
    public <T> void delete(T entity) {
        getSession().delete(entity);
    }

    @Override
    public <T> T get(Class<T> entityClass, Serializable id) {
        return (T) getSession().get(entityClass, id);
    }

    @Override
    public <T> List<T> listAll(Class<T> entityClass) {
        Criteria criteria = createCriteria(entityClass);
        return criteria.list();
    }

    @Override
    public <T> T getUniqueByProperty(Class<T> entityClass, String propertyName, Object value) {
        Assert.hasText(propertyName);
        return (T) createCriteria(entityClass, Restrictions.eq(propertyName, value)).uniqueResult();
    }

    @Override
    public <T> List<T> listByProperty(Class<T> entityClass, String propertyName, Object value) {
        Assert.hasText(propertyName);
        return (List<T>) createCriteria(entityClass, Restrictions.eq(propertyName, value)).list();
    }

    @Override
    public <T> T getUniqueByHql(String hql, Object... param) {
        T t = null;
        Query queryObject = getSession().createQuery(hql);
        if (param != null && param.length > 0) {
            for (int i = 0; i < param.length; i++) {
                queryObject.setParameter(i, param[i]);
            }
        }
        List<T> list = queryObject.list();
        if (list.size() == 1) {
            // getSession().flush();
            t = list.get(0);
        } else if (list.size() > 0) {
            throw new BusinessException("查询结果数:" + list.size() + "大于1");
        }
        return t;
    }

    @Override
    public <T> List<T> listBySql(String query) {
        Query querys = getSession().createSQLQuery(query);
        return querys.list();
    }

    @Override
    public <T> List<T> listByPropertyOrder(Class<T> entityClass, String propertyName, Object value, boolean isAsc) {
        Assert.hasText(propertyName);
        return createCriteria(entityClass, isAsc, Restrictions.eq(propertyName, value)).list();
    }

    @Override
    public void listByPage(CriteriaQuery cq, boolean ispage) {
        Criteria criteria = cq.getDetachedCriteria().getExecutableCriteria(getSession());
        CriteriaImpl impl = (CriteriaImpl) criteria;
        // 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
        Projection projection = impl.getProjection();
        final int allCounts = ((Long) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
        criteria.setProjection(projection);
        if (projection == null) {
            criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
        }
        // 排序
        Map<String, Object> ordermap = cq.getOrderMap();
        String sort = cq.getDataGrid().getSort();
        if (StringUtils.isNotBlank(sort)) {
            String[] sortArr = sort.split(",");
            String[] orderArr = cq.getDataGrid().getOrder().split(",");
            if (sortArr.length != orderArr.length && orderArr.length > 0) {
                for (int i = 0; i < sortArr.length; i++) {
                    if (SortDirection.asc.equals(SortDirection.toEnum(orderArr[0]))) {
                        ordermap.put(sortArr[i], SortDirection.asc);
                    } else {
                        ordermap.put(sortArr[i], SortDirection.desc);
                    }
                }
            } else if (sortArr.length == orderArr.length) {
                for (int i = 0; i < sortArr.length; i++) {
                    if (SortDirection.asc.equals(SortDirection.toEnum(orderArr[i]))) {
                        ordermap.put(sortArr[i], SortDirection.asc);
                    } else {
                        ordermap.put(sortArr[i], SortDirection.desc);
                    }
                }
            }
        }
        if (!ordermap.isEmpty() && ordermap.size() > 0) {
            cq.setOrder(ordermap);
        }
        // 每页显示数
        int pageSize = cq.getPageSize();
        int curPageNO = PageUtil.getcurPageNo(allCounts, cq.getCurPage(), pageSize);// 当前页
        int offset = PageUtil.getOffset(allCounts, curPageNO, pageSize);
        if (ispage) {// 是否分页
            criteria.setFirstResult(offset);
            criteria.setMaxResults(cq.getPageSize());
        } else {
            pageSize = allCounts;
        }
        List list = criteria.list();
        cq.getDataGrid().setResults(list);
        cq.getDataGrid().setTotal(allCounts);
    }

    @Override
    public <T> List<T> listByCriteria(CriteriaQuery cq) {
        Criteria criteria = cq.getDetachedCriteria().getExecutableCriteria(getSession());
        // 判断是否有排序字段
        if (cq.getOrderMap() != null) {
            cq.setOrder(cq.getOrderMap());
        }
        return criteria.list();
    }

    @Override
    public <T> List<T> listByHql(String hql, Object... param) {
        Query q = getSession().createQuery(hql);
        if (param != null && param.length > 0) {
            for (int i = 0; i < param.length; i++) {
                q.setParameter(i, param[i]);
            }
        }
        return q.list();
    }

    public Integer executeHql(String hql, Object... param) {
        Query q = getSession().createQuery(hql);
        if (param != null && param.length > 0) {
            for (int i = 0; i < param.length; i++) {
                q.setParameter(i, param[i]);
            }
        }
        return q.executeUpdate();
    }

    private <T> Criteria createCriteria(Class<T> entityClass) {
        Criteria criteria = getSession().createCriteria(entityClass);
        return criteria;
    }

    private <T> Criteria createCriteria(Class<T> entityClass, Criterion... criterions) {
        Criteria criteria = getSession().createCriteria(entityClass);
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    private <T> Criteria createCriteria(Class<T> entityClass, boolean isAsc, Criterion... criterions) {
        Criteria criteria = createCriteria(entityClass, criterions);
        if (isAsc) {
            criteria.addOrder(Order.asc("asc"));
        } else {
            criteria.addOrder(Order.desc("desc"));
        }
        return criteria;
    }

    @Override
    public List<Map<String, Object>> listBySql(String sql, Object... objs) {
        return this.jdbcTemplate.queryForList(sql, objs);
    }

    public <T> List<T> listBySql(final String sql, final Class<T> entityClass, Map<String, Object> parameters) {
        try {
            if (parameters != null) {
                return jdbcTemplate.query(sql, resultBeanMapper(entityClass), parameters);
            } else {
                return jdbcTemplate.query(sql, resultBeanMapper(entityClass));
            }
        } catch (Exception e) {
            throw new BusinessException(e);
        }
    }

    public Integer executeSql(String sql, Object... param) {
        return this.jdbcTemplate.update(sql, param);
    }

    protected <T> BeanPropertyRowMapper<T> resultBeanMapper(Class<T> entityClass) {
        return BeanPropertyRowMapper.newInstance(entityClass);
    }

}
